﻿using System.Collections;
using System.IO;

namespace theeg.DependencyAnalysis.Retrospective
{
    public class JsonWriter
    {
        private readonly TextWriter _writer;

        public JsonWriter(TextWriter writer)
        {
            _writer = writer;
        }

        public void WriteStartAttribute(string name)
        {
            _writer.Write("\"" + name + "\": ");
        }

        public void WriteEndAttribute()
        {
            _writer.Write(", ");
        }

        public void WriteValue(object value)
        {
            if (value is string)
            {
                _writer.Write("\"" + Escape("" + value) + "\"");
                return;
            }
            if (value is IEnumerable)
            {
                WriteStartArray();
                bool isFirst = true;
                foreach (object item in (IEnumerable)value)
                {
                    if (!isFirst)
                    {
                        _writer.Write(",\n");
                    }
                    else
                    {
                        isFirst = false;
                    }
                    if (item is IJsonSerializable)
                    {
                        ((IJsonSerializable)item).WriteJson(this);
                    }
                    else
                    {
                        WriteValue(item);
                    }
                }
                WriteEndArray();
                return;
            }
            if (value is IJsonSerializable)
            {
                ((IJsonSerializable)value).WriteJson(this);
                return;
            }
            if (IsNumber(value) || value is bool)
            {
                _writer.Write("" + value);
                return;
            }
            _writer.Write("\"" + Escape("" + value) + "\"");
        }

        public static bool IsNumber(object value)
        {
            return value is sbyte
                    || value is byte
                    || value is short
                    || value is ushort
                    || value is int
                    || value is uint
                    || value is long
                    || value is ulong
                    || value is float
                    || value is double
                    || value is decimal;
        }

        private string Escape(string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }
            value = value.Replace("\\", "\\\\");
            value = value.Replace("\"", "\\\"");
            return value;
        }

        public void WriteEndElement()
        {
            _writer.Write(" }");
        }

        public void WriteStartElement()
        {
            _writer.Write(" { ");
        }

        public void WriteEndArray()
        {
            _writer.Write(" ]");
        }

        public void WriteStartArray()
        {
            _writer.Write(" [ ");
        }
    }
}
